<?php declare(strict_types=1);

namespace application\units;

class Auth
{
    /** @var \application\units\Di */
    private $di;
    
    private $data           = [];
    private $allowResources = [];
    
    public function __construct(Di $di)
    {
        $this->di = $di;
    }
    
    public function setData(array $data)
    {
        $this->data = $data;
    }
    
    public function getData(string $key = null)
    {
        if ($key) {
            return $this->data[$key];
        } else {
            return $this->data;
        }
    }
    
    public function __get($name)
    {
        return $this->data[$name];
    }
    
    public function init(string $token): bool
    {
        $tokenData = explode('-', $token);
        
        if (count($tokenData) < 2) {
            return false;
        }
        
        $id    = $tokenData[0];
        $token = $tokenData[1];
        
        $admin = $this->di->db->find('select * from admin where id=:id', [
            'id' => $id,
        ]);
        
        if (!$admin || $admin['token'] !== $token)
            return false;
        
        unset($admin['password'], $admin['salt'], $admin['loginfailure']);
        
        $roles          = array_column($this->getRolesByAdminId($admin['id']), 'name', 'id');
        $admin['roles'] = $roles;
        $this->setData($admin);
        
        return true;
    }
    
    public function setAllowResources($allowResources)
    {
        $this->allowResources = $allowResources;
    }
    
    public function permission(string $resource): bool
    {
        if (in_array($resource, $this->allowResources)) {
            return true;
        }
        
        $resourceIds = $this->getResourceIdsByAdminId($this->data['id']);
        
        // 判断管理员超级管理员权限
        if (in_array(0, $resourceIds)) {
            return true;
        }
        
        $row = $this->di->db->find('select * from admin_resource where resource=:resource or path=:path', [
            'resource' => $resource,
            'path'     => str_replace('.', '/', $resource),
        ]);
        
        if (!$row) {
            return true;
        }
        
        if (in_array($row['id'], $resourceIds)) {
            return true;
        }
        
        return false;
    }
    
    public function isLogin(): bool
    {
        if ($this->data) {
            return true;
        } else {
            return false;
        }
    }
    
    public function getChildrenRoleAdminIds($adminId, bool $withself = false): array
    {
        $roleIds = $this->getChildrenRoleIds($adminId, $withself);
        return $this->getChildrenAdminIdsByRoleIds($roleIds, $adminId, $withself);
    }
    
    /**
     * @return array
     */
    public function getChildrenAdminIdsByRoleIds(array $roleIds, $adminId, bool $withself = false): array
    {
        if (!$roleIds) {
            if ($withself) {
                return [$adminId];
            }
            return [];
        }
        
        // 获取所有角色组关联的用户ID
        $adminIds = array_column($this->di->db->query('select admin_id from admin_role_access where role_id in (:role_id) group by admin_id', [
            'role_id' => $roleIds,
        ]), 'admin_id');
        
        if ($withself) {
            array_push($adminIds, $adminId);
        } else {
            $adminIds = array_values(array_filter($adminIds, function ($val) use ($adminId) {
                return $val != $adminId;
            }));
        }
        
        return $adminIds;
    }
    
    public function getChildrenRoleIds($adminId, bool $withself = false): array
    {
        // 获取管理员的角色分组
        $adminRoleIds = $this->getRoleIdsByAdminId($adminId);
        $roles        = $this->getRoles();
        $tree         = $this->di->tree->init($roles);
        $data         = [];
        // 获取管理员的角色分组的下级分组
        foreach ($adminRoleIds as $roleId) {
            foreach ($tree->getChildrenIds($roleId, $withself) as $item) {
                array_push($data, $item);
            }
        }
        
        return array_unique($data);
    }
    
    public function getChildrenRoleIdsByRoleId($roleId, $withself = false): array
    {
        // 获取管理员的角色分组的下级分组
        $roles = $this->getRoles();
        return $this->di->tree->init($roles)->getChildrenIds($roleId, $withself);
    }
    
    public function getRolesByAdminId($adminId): array
    {
        // 获取管理员的角色分组
        return $this->di->db->query('select ag.* from admin_role_access aga join admin_role ag on aga.role_id=ag.id where aga.admin_id=:admin_id', [
            'admin_id' => $adminId,
        ]);
    }
    
    public function getRoleIdsByAdminId($adminId): array
    {
        // 获取管理员的角色分组
        return array_column($this->di->db->query('select role_id from admin_role_access where admin_id=:admin_id', [
            'admin_id' => $adminId,
        ]), 'role_id');
    }
    
    public function getResource($adminId = null): array
    {
        $where     = '';
        $bindValue = [];
        if ($adminId !== null) {
            $resourceIds = $this->getResourceIdsByAdminId($adminId);
            
            // admin_group_rule_access表中rule_id=0表示是超级管理员角色，超级管理员不需要加条件
            if (!in_array(0, $resourceIds)) {
                $where           = ' and id in (:id)';
                $bindValue['id'] = $resourceIds;
            }
        }
        
        $rows = $this->di->db->query(sprintf('select * from admin_resource where status=1 %s', $where), $bindValue);
        
        
        foreach ($rows as &$row) {
            $row['path']     = '/' . trim($row['path'], '/');
            $row['resource'] = $row['resource'] ?: str_replace('/', '.', trim($row['path'], '/'));
        }
        
        return $rows;
    }
    
    public function getResourceIdsByAdminId($adminId): array
    {
        return array_column($this->di->db->query('select agra.resource_id from admin_role_access aga
                        join admin_role_resource_access agra
                        on aga.role_id=agra.role_id
                        where aga.admin_id=:admin_id', [
            'admin_id' => $adminId,
        ]), 'resource_id');
    }
    
    private function getRoles(): array
    {
        return $this->di->db->query('select id,pid,name from admin_role');
    }
    
    public function log(array $adminOperationLog)
    {
        $this->di->db->execute('insert into admin_operation_log (
                                    admin_id, username, url, title, content, ip, useragent, create_time
                                 ) VALUES (
                                    :admin_id, :username, :url, :title, :content, :ip, :useragent, :create_time
                                 )', [
            'admin_id'    => $adminOperationLog['admin_id'],
            'username'    => $adminOperationLog['username'],
            'url'         => $adminOperationLog['url'],
            'title'       => $adminOperationLog['title'],
            'content'     => $adminOperationLog['content'],
            'ip'          => $adminOperationLog['ip'],
            'useragent'   => $adminOperationLog['useragent'],
            'create_time' => $adminOperationLog['create_time'],
        ]);
    }
}